#include <binary_protocol.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include "mc.h"

//uint32_t u_global_req_id;
//int u_sock_fd;

/*
void create_udp_connection(char *host, int port_no) {
    u_sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    //set_nonblock(u_sock_fd);

    if (u_sock_fd < 0) {
        fprintf(stderr, "u_sock_fd < 0\n");
        exit(EXIT_FAILURE);
    }

    u_server = gethostbyname(host);
    if (NULL == u_server) {
        fprintf(stderr, "u_server is null\n");
        exit(EXIT_FAILURE);
    }

    memset((char *) &u_server_addr, 0, sizeof(u_server_addr));
    u_server_addr.sin_family = AF_INET;
    u_server_addr.sin_port = htons(port_no);
    memcpy((void *)&u_server_addr.sin_addr, u_server->h_addr_list[0], u_server->h_length);
    u_global_req_id = 0;
}

void binary_set_or_noop(char *key, char *value, bool is_noop) {
    char u_s_buf[MAX_SET_BUF_SIZE], u_recv_buf[MAX_SET_BUF_SIZE];
    int body_bytes = 0;
    u_global_req_id++;
    size_t udp_header_len = 0;
    if (enable_udp) {
        to_udp_header(u_s_buf, u_global_req_id);
        udp_header_len = 8;
    }

    int h_bytes = 24;
    int extra_bytes = 0;
    if (!is_noop) {
        extra_bytes = 8;
    }
    if (is_noop) {
        binary_noop(u_s_buf+udp_header_len);
    } else {
        create_binary_set_request(u_s_buf + udp_header_len, key, value, &body_bytes);
    }

    ssize_t sent;
    socklen_t addr_len = sizeof(u_server_addr);
    if (enable_udp) {
        sent = sendto(u_sock_fd, u_s_buf, body_bytes + h_bytes + extra_bytes + udp_header_len, 0,
                      (struct sockaddr *) &u_server_addr, addr_len);
    } else {
        sent = sendto(tcp_fd, u_s_buf, body_bytes + h_bytes + extra_bytes + udp_header_len, 0, NULL, 0);
    }
    if (sent < body_bytes+h_bytes + extra_bytes +udp_header_len) {
        fprintf(stderr, "send_set sendto error sent=%lu\n", sent);
        exit(EXIT_FAILURE);
    } else {
        fprintf(stderr, "udp_send_set sent=%zu u_s_buf=> %lu\n", sent,
                body_bytes+h_bytes+extra_bytes+udp_header_len);
    }
    //ensure_readable(u_sock_fd); //todo check

    fprintf(stderr, "try to receive from\n");
    if (enable_udp) {
        sent = recvfrom(u_sock_fd, u_recv_buf, udp_header_len+h_bytes, 0,
                        (struct sockaddr *) &u_server_addr, &addr_len);
    } else {
        sent = recvfrom(tcp_fd, u_recv_buf, udp_header_len+h_bytes, 0, NULL, 0);
    }
    //u_recv_buf[MAX_SET_BUF_SIZE-1] = '\0';
    fprintf(stderr, "after recvfrom received=> %zd\n", sent);
    if (sent < udp_header_len+h_bytes) {
        fprintf(stderr, "send_set recvfrom error\n");
        exit(EXIT_FAILURE);
    } else {
        if (enable_udp) {
            parse_udp_header(u_recv_buf);
        }
        binary_header_t *h = (binary_header_t *)(u_recv_buf + udp_header_len);
        fprintf(stderr, "body_len=%d set_op status=%d\n", h->body_len, h->status);
    }
}
 */

/*
void binary_get(char **keys, int batch_sz, bool mget_mode) {
    char u_buf[MAX_GET_BUF_SIZE], u_r_buf[MAX_GET_BUF_SIZE];
    int u_req_len = 0;
    u_global_req_id++;
    if (enable_udp) {
        to_udp_header(u_buf, u_global_req_id); //8 bytes
        u_req_len += 8;
    }

    protocol_binary_command command;
    for (int ii = 0; ii < batch_sz; ii++) {
        // if (ii == batch_sz-1)
        // 	command = CMD_GETK;
        // else
        // 	command = CMD_GETKQ;

        // u_req_len += compose_binary_get(u_buf + u_req_len, keys[ii], command, ii+10);

        // bget
        u_req_len += compose_binary_bget(u_buf + u_req_len, keys[ii], (uint8_t)(ii+111));
    }
    socklen_t u_server_len = sizeof(u_server_addr);

    binary_noop(u_buf + u_req_len);
    int noop_h_l = 24;
    u_req_len += noop_h_l;

    ssize_t sent;
    if (enable_udp) {
        sent = sendto(u_sock_fd, u_buf, (size_t) u_req_len, 0,
                      (struct sockaddr *) &u_server_addr, u_server_len);
    } else {
        sent = sendto(tcp_fd, u_buf, (size_t) u_req_len, 0, NULL, 0);
    }
    if (sent < u_req_len) {
        perror("send get_request error");
        exit(EXIT_FAILURE);
    } else {
        fprintf(stderr, "get_request sent=%zu u_req_len=> %d\n\n", sent, u_req_len);
    }

    //sleep(1);
    ssize_t _res = 1;
    size_t reply_len = 0;

    int count = num_keys;
    //while (count > 0 && _res > 0) {
    if (enable_udp) {
        _res = recvfrom(u_sock_fd, u_r_buf+reply_len, MAX_GET_BUF_SIZE-reply_len, 0,
                        (struct sockaddr *) &u_server_addr, &u_server_len);
    } else {
        _res = recvfrom(tcp_fd, u_r_buf+reply_len, MAX_GET_BUF_SIZE-reply_len, 0, NULL, NULL);
    }
    u_r_buf[MAX_GET_BUF_SIZE - 1] = '\0';
    count--;
    if (_res < 0) {
        fprintf(stderr, "_res=%zd exit\n", _res);
        exit(EXIT_FAILURE);
        //break;
    } else {
        reply_len += _res;
        fprintf(stderr, "reply=> %zd\n", _res);
    }
    //}


    //  for (int jj=0; jj<batch_sz; jj++) {
    //      memset(u_r_buf, 0, MAX_GET_BUF_SIZE);

    //      // read in udp header
    //      recv_len = recvfrom(u_sock_fd, u_r_buf, 8+24+30, 0,
    //                                  (struct sockaddr *) &u_server_addr, &u_server_len);
    //      fprintf(stderr, "udp_send_get_request recv_len=%zu\n", recv_len);
    //      if (recv_len < 62)
    //          exit(EXIT_FAILURE);

    //      u_r_buf[62] = '\0'; //<== key step
    //      parse_udp_header(u_r_buf);

    //      // recv_len = recvfrom(u_sock_fd, u_r_buf, 24, 0,
    //      // 							(struct sockaddr *) &u_server_addr, &u_server_len);
    //      // fprintf(stderr, "udp_send_get_request recv_len=%zu\n", recv_len);
    //      // if (recv_len < 24)
    //      // 	exit(EXIT_FAILURE);

    //      binary_header_t *h = (binary_header_t *) (u_r_buf+8);
    //      //b_len = ntohl(h->body_len);
    //      fprintf(stderr, "klen=%u extlen=%u op=%d status=%d blen=%u/%u read_bytes\n",
    //              ntohs(h->key_len), h->extra_len, h->opcode, h->status, b_len, h->body_len);
    //      for (int ii = 8; ii < 32; ++ii) {
    //          if (ii % 4 == 0) {
    //              fprintf(stderr, "\n>%d ", u_sock_fd);
    //          }
    //          fprintf(stderr, " 0x%02x", u_r_buf[ii]);
    //      }

    //      // recv_len = recvfrom(u_sock_fd, u_r_buf+24, b_len, 0,
    //      // 					(struct sockaddr *) &u_server_addr, &u_server_len);
    //      // if (recv_len < b_len)
    //      // 	exit(EXIT_FAILURE);

    //      for (int ii = 32; ii < 32+b_len; ++ii) {
    //          if (ii % 4 == 0) {
    //              fprintf(stderr, "\n>>%d ", u_sock_fd);
            }
            fprintf(stderr, " 0x%02x", u_r_buf[ii]);
        }
        fprintf(stderr, "  <= jj=%d\n", jj);
        //read_bytes += (24+b_len);
    }

    fprintf(stderr, "finish parse udp_send_get_request\n");
}

void get_mget_response(int fd, int batch_sz) {
    char response[32];
    char resp_body[32];
    ssize_t res;
	int i=0;
    for ( i=0; i<batch_sz; i++) {
        memset(response, 0, 32);
        memset(resp_body, 0, 32);
        //ensure_readable(fd, poll_wait_sec);
        res = read(fd, response, 24);
        if (res == 24) {
            binary_header_t *h = (binary_header_t *)response;
            uint32_t b_len = ntohl(h->body_len);
            fprintf(stdout, "klen=%u extlen=%u op=%d status=%d blen=%u\n",
                    ntohs(h->key_len), h->extra_len, h->opcode, h->status, b_len);
            res = read(fd, response, b_len);
            if (res == b_len) {
	int ii=0;
                for (int ii = 0; ii < b_len; ++ii) {
                    if (ii % 4 == 0) {
                        fprintf(stderr, "\n>%d  ", fd);
                    }
                    fprintf(stderr, " 0x%02x", response[ii]);
                }
                fprintf(stderr, "\n");
                for (int ii = 0; ii < b_len; ++ii) {
                    fprintf(stderr, "%c", response[ii]);
                }
                fprintf(stderr, "\n");
            } else {
                fprintf(stderr, "error read %d body\n", i);
            }
        } else {
            fprintf(stderr, "error read %d header res=%zd\n", i, res);
        }
    }
}
*/

size_t send_request(int sfd, char *request, size_t len) {
    ssize_t sent = sendto(sfd, request, len, 0, NULL, 0);
    if (sent < 0) {
        fprintf(stderr, "send_request failure\n");
        exit(EXIT_FAILURE);
    } else if (sent < len) {
        fprintf(stderr, "send_len %zd < expected %zd\n", sent, len);
    }
    return sent;
}

size_t recv_request(int sfd, char *recv_buf, size_t len) {
    ssize_t recv = recvfrom(sfd, recv_buf, len, 0, NULL, 0);
    if (recv < 0) {
        fprintf(stderr, "recv_request failure\n");
        exit(EXIT_FAILURE);
    } else if (recv < len) {
        fprintf(stderr, "recv_len %zd < expected %zd\n", recv, len);
    }
    return recv;
}

void print_buf_info(char *buf, int len) {
int i=0;
    for ( i=0; i<len; i++) {
        if (i % 4 == 0) {
            fprintf(stderr, "\n> ");
        }
        fprintf(stderr, " 0x%02x", buf[i]);
    }
    fprintf(stderr, "\n");
}

void print_get_resp_info(char *buf, int len) {
    binary_get_resp_header *bh = (binary_get_resp_header *)buf;
    size_t resp_len = sizeof(binary_header_t) + ntohl(bh->header.body_len);
    size_t read_len = 0;

    while (read_len < len) {
	int ii=0;
        for ( ii=read_len; ii<read_len+resp_len; ii++) {
            if (ii % 4 == 0) {
                fprintf(stderr, "\n> ");
            }
            fprintf(stderr, " 0x%02x", buf[ii]);
        }
        fprintf(stderr, "\n");
        bh = (binary_get_resp_header *)(buf+read_len);
        fprintf(stderr, "reply opaque (port=%u ip=%u)\n",
                ntohs(bh->header.src_ip), ntohs(bh->header.src_port));
        read_len += resp_len;
    }
}

